// Copyright (C) ZHENG Zhong <heavyzheng nospam-at gmail D0T com>
// http://www.zhengzhong.net/
//
// $Id: parser.hpp 318 2009-03-02 14:03:21Z heavyzheng $
//

#ifndef ZZHENG_XML_SAX_LIBXML2_PARSER_HPP_20090122__
#define ZZHENG_XML_SAX_LIBXML2_PARSER_HPP_20090122__

#include <zzheng/xml/config.hpp>
#include <zzheng/xml/sax/types.hpp>
#include <zzheng/xml/sax/exception.hpp>
#include <zzheng/xml/sax/content_handler.hpp>
#include <zzheng/xml/sax/error_handler.hpp>

#include <zzheng/xml/sax/libxml2/base.hpp>

#include <boost/noncopyable.hpp>

#include <libxml/xmlstring.h>
#include <libxml/xmlerror.h>

#include <string>
#include <map>
#include <vector>

namespace zzheng {
namespace xml {
namespace sax {
namespace libxml2_ {

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // libxml2 SAX callbacks
    //

    //! Libxml2 SAX startDocumentSAXFunc.
    void begin_doc(void* user_data);

    //! Libxml2 SAX endDocumentSAXFunc.
    void end_doc(void* user_data);

    //! Libxml2 SAX startElementSAXFunc.
    void begin_elem(void* user_data, const xmlChar* qname, const xmlChar** attrs);

    //! Libxml2 SAX endElementSAXFunc.
    void end_elem(void* user_data, const xmlChar* qname);

    //! Libxml2 SAX charactersSAXFunc.
    void characters(void* user_data, const xmlChar* chars, int length);

    //! Libxml2 SAX cdataBlockSAXFunc.
    void cdata_block(void* user_data, const xmlChar* cdata, int length);

    //! Libxml2 SAX ignorableWhitespaceSAXFunc.
    void ignorable_whitespace(void* user_data, const xmlChar* spaces, int length);

    //! Libxml2 SAX commentSAXFunc.
    void comment(void* user_data, const xmlChar* comment);

    //! Libxml2 SAX xmlStructuredErrorFunc.
    //! See: http://www.xmlsoft.org/html/libxml-xmlerror.html#xmlError
    void structured_error(void* user_data, xmlError* err);


    ////////////////////////////////////////////////////////////////////////////////////////////////
    // parser class
    //

    //! This class represents an XML SAX parser. It is used to parse an XML file and invoke SAX
    //! callback functions. For more information about SAX XML parsing, check the SAX project at:
    //! http://www.saxproject.org/
    class parser: private base, private boost::noncopyable {

        ////////////////////////////////////////////////////////////////////////////////////////////
        // declaration of friends
        //

        friend void begin_doc(void*);
        friend void end_doc(void*);
        friend void begin_elem(void*, const xmlChar*, const xmlChar**);
        friend void end_elem(void*, const xmlChar*);
        friend void characters(void*, const xmlChar*, int);
        friend void cdata_block(void*, const xmlChar*, int);
        friend void ignorable_whitespace(void*, const xmlChar*, int);
        friend void comment(void*, const xmlChar*);
        friend void structured_error(void*, xmlError*);

    public:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // constructor and destructor
        //

        //! Default constructor.
        explicit parser();

        //! Destructor.
        ~parser();

    public:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // public member functions
        //

        //! Registers a content handler to the parser. If user does not register a content handler,
        //! all content events reported by the SAX parser will be silently ignored.
        //! \param handler  the SAX content handler to register.
        void set_content_handler(content_handler* handler);

        //! Registers an error handler to the parser. If user does not register an error handler,
        //! all error events reported by the SAX parser will be silently ignored; however, normal
        //! processing may not continue. It is highly recommended that all SAX applications
        //! implement an error handler to avoid unexpected bugs.
        //! \param handler  the SAX error handler to register.
        void set_error_handler(error_handler* handler);

        //! Sets the value to a feature flag to enable or disable the feature.
        //! \param name    the name of the feature.
        //! \param enable  the value of the feature to set, true to enable, false to disable.
        void set_feature(const std::string& name, bool enable);

        //! Returns the value of a feature flag (true means enabled, false means disabled).
        //! \param name  the name of the feature.
        //! \return true if this feature is enabled, false otherwise.
        bool get_feature(const std::string& name) const;

        bool is_ns_enabled() const;

        bool is_ns_prefixes_enabled() const;

        //! Parses an XML file and invokes SAX callbacks.
        //! \param file_name  the XML file name.
        void parse_file(const std::string& file_name);

        //! Parses a string containing the XML and invokes SAX callbacks.
        //! \param str  the XML string to parse.
        void parse_string(const char* str);

    private:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // private callback member functions
        //

        //! Receives notification of the beginning of an XML document.
        void begin_doc();

        //! Receives notification of the end of an XML document.
        void end_doc();

        //! Receives notification of the beginning of a namespace scope.
        //! \param prefix  the namespace prefix being declared. An empty string is used for the
        //!                default element namespace, which has no prefix.
        //! \param uri     the namespace URI the prefix is mapped to.
        void begin_ns(const std::string& prefix, const std::string& uri);

        //! Receives notification of the end of a namespace scope.
        //! \param prefix  the namespace prefix that was being mapped, or an empty string when a
        //!                default mapping scope ends.
        void end_ns(const std::string& prefix);

        //! Receives notification of the beginning of an element.
        //! \param name   the local name of this element.
        //! \param uri    the namespace URI of this element.
        //! \param qname  the qualified (prefixed) name of this element.
        //! \param attrs  the attribute list associated with this element.
        void begin_elem(const std::string& name,
                        const std::string& uri,
                        const std::string& qname,
                        const attribute_list& attrs);

        //! Receives notification of the end of an element.
        //! \param name   the local name of this element.
        //! \param uri    the namespace URI of this element.
        //! \param qname  the qualified (prefixed) name of this element.
        void end_elem(const std::string& name, const std::string& uri, const std::string& qname);

        void characters(const char* chars, size_type start, size_type length);

        void ignorable_whitespace(const char* chars, size_type start, size_type length);

        //! Receives notification of an XML comment.
        //! \param chars  the XML comment.
        //! \param start  the starting position in the XML comment.
        //! \param length  the length of the XML comment.
        void comment(const char* chars, size_type start, size_type length);

        void warning(const error_info& info);

        void error(const error_info& info);

        void fatal(const error_info& info);


    private:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // private utility member functions
        //

        std::string find_uri(const std::string& prefix) const;

        const std::vector<std::string>& get_deprecated_prefixes() const;

    private:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // private struct and typedef's
        //

        //! This class represents an element in the element stack.
        struct element {

            explicit element(): qname_(), prefixes_() {
                // Do nothing.
            }

            // Use auto-generated copy constructor.
            // Use auto-generated destructor.
            // Use auto-generated copy assignment.

            std::string              qname_;
            std::vector<std::string> prefixes_;

        }; // struct element

        //! The element stack.
        typedef std::vector<element> element_stack;

        //! The namespace prefix mappings: key is the prefix, value is a stack of URIs.
        typedef std::map<std::string, std::vector<std::string> > prefix_uris_map;

        typedef std::map<const std::string, bool> feature_map;

    private:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // private members
        //

        element_stack    stack_;           //!< The element stack.
        element          popped_;          //!< The element previously popped out.
        prefix_uris_map  namespaces_;      //!< The namespace prefix-URI mapping registry.
        feature_map      features_;        //!< The feature and value map.
        content_handler* content_handler_; //!< Pointer to content handler.
        error_handler*   error_handler_;   //!< Pointer to error handler.

    }; // class parser


} // namespace zzheng::xml::sax::libxml2_
} // namespace zzheng::xml::sax
} // namespace zzheng::xml
} // namespace zzheng


#endif // ZZHENG_XML_SAX_LIBXML2_PARSER_HPP_20090122__




